/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.dx.cf.code;

import com.android.dx.rop.cst.CstType;
import com.android.dx.rop.type.Prototype;
import com.android.dx.rop.type.Type;
import com.android.dx.rop.type.TypeBearer;
import com.android.dx.util.Hex;

/**
 * {@link Machine} which keeps track of known values but does not do
 * smart/realistic reference type calculations.
 */
public class ValueAwareMachine extends BaseMachine {

	/**
	 * Constructs an instance.
	 * 
	 * @param prototype
	 *            {@code non-null;} the prototype for the associated method
	 */
	public ValueAwareMachine(Prototype prototype) {
		super(prototype);
	}

	/** {@inheritDoc} */
	public void run(Frame frame, int offset, int opcode) {
		switch (opcode) {
		case ByteOps.NOP:
		case ByteOps.IASTORE:
		case ByteOps.POP:
		case ByteOps.POP2:
		case ByteOps.IFEQ:
		case ByteOps.IFNE:
		case ByteOps.IFLT:
		case ByteOps.IFGE:
		case ByteOps.IFGT:
		case ByteOps.IFLE:
		case ByteOps.IF_ICMPEQ:
		case ByteOps.IF_ICMPNE:
		case ByteOps.IF_ICMPLT:
		case ByteOps.IF_ICMPGE:
		case ByteOps.IF_ICMPGT:
		case ByteOps.IF_ICMPLE:
		case ByteOps.IF_ACMPEQ:
		case ByteOps.IF_ACMPNE:
		case ByteOps.GOTO:
		case ByteOps.RET:
		case ByteOps.LOOKUPSWITCH:
		case ByteOps.IRETURN:
		case ByteOps.RETURN:
		case ByteOps.PUTSTATIC:
		case ByteOps.PUTFIELD:
		case ByteOps.ATHROW:
		case ByteOps.MONITORENTER:
		case ByteOps.MONITOREXIT:
		case ByteOps.IFNULL:
		case ByteOps.IFNONNULL: {
			// Nothing to do for these ops in this class.
			clearResult();
			break;
		}
		case ByteOps.LDC:
		case ByteOps.LDC2_W: {
			setResult((TypeBearer) getAuxCst());
			break;
		}
		case ByteOps.ILOAD:
		case ByteOps.ISTORE: {
			setResult(arg(0));
			break;
		}
		case ByteOps.IALOAD:
		case ByteOps.IADD:
		case ByteOps.ISUB:
		case ByteOps.IMUL:
		case ByteOps.IDIV:
		case ByteOps.IREM:
		case ByteOps.INEG:
		case ByteOps.ISHL:
		case ByteOps.ISHR:
		case ByteOps.IUSHR:
		case ByteOps.IAND:
		case ByteOps.IOR:
		case ByteOps.IXOR:
		case ByteOps.IINC:
		case ByteOps.I2L:
		case ByteOps.I2F:
		case ByteOps.I2D:
		case ByteOps.L2I:
		case ByteOps.L2F:
		case ByteOps.L2D:
		case ByteOps.F2I:
		case ByteOps.F2L:
		case ByteOps.F2D:
		case ByteOps.D2I:
		case ByteOps.D2L:
		case ByteOps.D2F:
		case ByteOps.I2B:
		case ByteOps.I2C:
		case ByteOps.I2S:
		case ByteOps.LCMP:
		case ByteOps.FCMPL:
		case ByteOps.FCMPG:
		case ByteOps.DCMPL:
		case ByteOps.DCMPG:
		case ByteOps.ARRAYLENGTH: {
			setResult(getAuxType());
			break;
		}
		case ByteOps.DUP:
		case ByteOps.DUP_X1:
		case ByteOps.DUP_X2:
		case ByteOps.DUP2:
		case ByteOps.DUP2_X1:
		case ByteOps.DUP2_X2:
		case ByteOps.SWAP: {
			clearResult();
			for (int pattern = getAuxInt(); pattern != 0; pattern >>= 4) {
				int which = (pattern & 0x0f) - 1;
				addResult(arg(which));
			}
			break;
		}

		case ByteOps.JSR: {
			setResult(new ReturnAddress(getAuxTarget()));
			break;
		}
		case ByteOps.GETSTATIC:
		case ByteOps.GETFIELD:
		case ByteOps.INVOKEVIRTUAL:
		case ByteOps.INVOKESTATIC:
		case ByteOps.INVOKEINTERFACE: {
			Type type = ((TypeBearer) getAuxCst()).getType();
			if (type == Type.VOID) {
				clearResult();
			} else {
				setResult(type);
			}
			break;
		}
		case ByteOps.INVOKESPECIAL: {
			Type thisType = arg(0).getType();
			if (thisType.isUninitialized()) {
				frame.makeInitialized(thisType);
			}
			Type type = ((TypeBearer) getAuxCst()).getType();
			if (type == Type.VOID) {
				clearResult();
			} else {
				setResult(type);
			}
			break;
		}
		case ByteOps.NEW: {
			Type type = ((CstType) getAuxCst()).getClassType();
			setResult(type.asUninitialized(offset));
			break;
		}
		case ByteOps.NEWARRAY:
		case ByteOps.CHECKCAST:
		case ByteOps.MULTIANEWARRAY: {
			Type type = ((CstType) getAuxCst()).getClassType();
			setResult(type);
			break;
		}
		case ByteOps.ANEWARRAY: {
			Type type = ((CstType) getAuxCst()).getClassType();
			setResult(type.getArrayType());
			break;
		}
		case ByteOps.INSTANCEOF: {
			setResult(Type.INT);
			break;
		}
		default: {
			throw new RuntimeException("shouldn't happen: " + Hex.u1(opcode));
		}
		}

		storeResults(frame);
	}
}
